package com.dana.judge.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.core.constants.Constants;
import com.core.constants.JudgeConstants;
import com.core.enums.CodeRunStatus;
import com.dana.api.domain.UserExeResult;
import com.dana.api.domain.dto.JudgeSubmitDTO;
import com.dana.api.domain.vo.UserQuestionResultVO;
import com.dana.judge.domain.SandBoxExecuteResult;
import com.dana.judge.domain.UserSubmit;
import com.dana.judge.mapper.UserSubmitMapper;
import com.dana.judge.service.IJudgeService;
import com.dana.judge.service.ISandboxPoolService;
import com.dana.judge.service.ISandboxService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class JudgeServiceImpl implements IJudgeService {
    @Autowired
    private ISandboxService sandboxService;

    @Autowired
    private UserSubmitMapper userSubmitMapper;

    @Autowired
    private ISandboxPoolService sandboxPoolService;

    @Override
    public UserQuestionResultVO doJudgeJavaCode(JudgeSubmitDTO judgeSubmitDTO) {
        //获取执行结果sandBoxExecuteResult
        SandBoxExecuteResult sandBoxExecuteResult =
                sandboxPoolService.exeJavaCode(judgeSubmitDTO.getUserId(),judgeSubmitDTO.getUserCode(), judgeSubmitDTO.getInputList());
        UserQuestionResultVO userQuestionResultVO = new UserQuestionResultVO();
        //执行成功需要返回的数据
        if (sandBoxExecuteResult != null && CodeRunStatus.SUCCEED.equals(sandBoxExecuteResult.getRunStatus())) {
            userQuestionResultVO = doJudge(judgeSubmitDTO, sandBoxExecuteResult, userQuestionResultVO);

        } else {//执行失败需要返回的数据
            //是否通过: 0-未通过 1-通过
            userQuestionResultVO.setPass(Constants.FALSE);
            if (sandBoxExecuteResult != null) {
                //返回错误信息
                userQuestionResultVO.setErrorMsg(sandBoxExecuteResult.getExeMessage());
            } else {
                //发生未知错误的处理
                userQuestionResultVO.setErrorMsg(CodeRunStatus.UNKNOWN_FAILED.getMsg());
            }
            //获得的分数
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
        }
        //无论答题正确与否都需要保存
        saveUserSubmit(judgeSubmitDTO, userQuestionResultVO);
        return userQuestionResultVO;
    }

    private static UserQuestionResultVO doJudge(JudgeSubmitDTO judgeSubmitDTO,
                                                SandBoxExecuteResult sandBoxExecuteResult,
                                                UserQuestionResultVO userQuestionResultVO) {
        List<String> exeOutputList = sandBoxExecuteResult.getOutputList(); //实际输出
        List<String> outputList = judgeSubmitDTO.getOutputList(); //预期输出
        if (exeOutputList.size() != outputList.size()) {
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setErrorMsg(CodeRunStatus.NOT_ALL_PASSED.getMsg());
            return userQuestionResultVO;
        }

        List<UserExeResult> userExeResultList = new ArrayList<>();

        boolean passed = resultCompare(judgeSubmitDTO, outputList, exeOutputList, userExeResultList);
        return assmbleUserQuestionResultVO(judgeSubmitDTO, sandBoxExecuteResult,
                userQuestionResultVO, userExeResultList, passed);

    }

    private static UserQuestionResultVO assmbleUserQuestionResultVO(JudgeSubmitDTO judgeSubmitDTO,
                                                                    SandBoxExecuteResult sandBoxExecuteResult,
                                                                    UserQuestionResultVO userQuestionResultVO,
                                                                    List<UserExeResult> userExeResultList, boolean passed) {
        userQuestionResultVO.setUserExeResultList(userExeResultList);
        if (!passed) {
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setErrorMsg(CodeRunStatus.NOT_ALL_PASSED.getMsg());
            return userQuestionResultVO;
        }

        //空间限制
        if (sandBoxExecuteResult.getUseMemory() > judgeSubmitDTO.getSpaceLimit()) {
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setErrorMsg(CodeRunStatus.OUT_OF_MEMORY.getMsg());
            return userQuestionResultVO;
        }

        //时间限制
        if (sandBoxExecuteResult.getUseTime() > judgeSubmitDTO.getTimeLimit()) {
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setErrorMsg(CodeRunStatus.OUT_OF_TIME.getMsg());
            return userQuestionResultVO;

        }

        //走到这里就是比对成功
        userQuestionResultVO.setPass(Constants.TRUE);
        int score = judgeSubmitDTO.getDifficulty() * JudgeConstants.DEFAULT_SCORE;
        userQuestionResultVO.setScore(score);
        return userQuestionResultVO;
    }

    private static boolean resultCompare(JudgeSubmitDTO judgeSubmitDTO,
                                         List<String> outputList,
                                         List<String> exeOutputList,
                                         List<UserExeResult> userExeResultList) {
        boolean passed = true;
        for (int index = 0; index < outputList.size(); index++) {
            String output = outputList.get(index);//预期输出
            String exeOutput = exeOutputList.get(index);//实际输出
            String input = judgeSubmitDTO.getInputList().get(index);
            UserExeResult userExeResult = new UserExeResult();
            userExeResult.setInput(input);
            userExeResult.setOutput(output);
            userExeResult.setExeOutput(exeOutput);
            userExeResultList.add(userExeResult);
            //判断期望输出和实际输出是否相等
            if (!output.equals(exeOutput)) {
                passed = false;
            }
        }
        return passed;
    }

    private void saveUserSubmit(JudgeSubmitDTO judgeSubmitDTO, UserQuestionResultVO userQuestionResultVO) {
        //执行结果存储到数据库(只保留最近的一次的答题记录)
        UserSubmit userSubmit = new UserSubmit();
        BeanUtil.copyProperties(userQuestionResultVO, userSubmit);
        userSubmit.setUserId(judgeSubmitDTO.getUserId());
        userSubmit.setQuestionId(judgeSubmitDTO.getQuestionId());
        userSubmit.setExamId(judgeSubmitDTO.getExamId());
        userSubmit.setCreateBy(judgeSubmitDTO.getUserId());
        userSubmit.setCaseJudgeRes(JSON.toJSONString(userQuestionResultVO.getUserExeResultList()));
        userSubmit.setProgramType(judgeSubmitDTO.getProgramType());
        userSubmit.setUserCode(judgeSubmitDTO.getUserCode());

        //根据用户id,题目id,竞赛id(如果不是竞赛可不传)确定删除哪一条答题记录,只保留最近一次答题记录
        userSubmitMapper
                .delete(new LambdaQueryWrapper<UserSubmit>()
                .eq(UserSubmit::getUserId, judgeSubmitDTO.getUserId())
                .eq(UserSubmit::getQuestionId, judgeSubmitDTO.getQuestionId())
                .isNull(judgeSubmitDTO.getExamId()==null,UserSubmit::getExamId)
                .eq(judgeSubmitDTO.getExamId()!=null,UserSubmit::getExamId, judgeSubmitDTO.getExamId()));
        //save
        userSubmitMapper.insert(userSubmit);
    }
}
